{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "g_nWetWWd_ns"
   },
   "source": [
    "##### Copyright 2018 The TensorFlow Authors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "2pHVBk_seED1"
   },
   "outputs": [],
   "source": [
    "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "colab": {},
    "colab_type": "code",
    "id": "N_fMsQ-N8I7j"
   },
   "outputs": [],
   "source": [
    "#@title MIT License\n",
    "#\n",
    "# Copyright (c) 2017 François Chollet\n",
    "#\n",
    "# Permission is hereby granted, free of charge, to any person obtaining a\n",
    "# copy of this software and associated documentation files (the \"Software\"),\n",
    "# to deal in the Software without restriction, including without limitation\n",
    "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
    "# and/or sell copies of the Software, and to permit persons to whom the\n",
    "# Software is furnished to do so, subject to the following conditions:\n",
    "#\n",
    "# The above copyright notice and this permission notice shall be included in\n",
    "# all copies or substantial portions of the Software.\n",
    "#\n",
    "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
    "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
    "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
    "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
    "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
    "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
    "# DEALINGS IN THE SOFTWARE."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "pZJ3uY9O17VN"
   },
   "source": [
    "# Сохраняй и загружай модели"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "M4Ata7_wMul1"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/save_and_restore_models\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Читай на TensorFlow.org</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/tutorials/keras/save_and_restore_models.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запусти в Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/tutorials/keras/save_and_restore_models.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучай код на GitHub</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "mBdde4YJeJKF"
   },
   "source": [
    "Прогресс обучения моделей можно сохранять во время и после обучения: тренировку можно возобновить с того места, где ты остановился. Это обычно помогает избежать долгих бесперервыных сессий обучения. Сохраняя модель, ты также можешь поделиться ею с другими, чтобы они могли воспроизвести результаты ее работы. Большинство практиков машинного обучения помимо самой модели и использованных техник также публикуют:\n",
    "\n",
    "* Код, при помощи которого обучалась модель\n",
    "* Тренировочные веса, или параметры модели\n",
    "\n",
    "Публикация этих данных помогает другим понять как работает модель, а также они смогут проверить как она ведет себя с новыми данными.\n",
    "\n",
    "Внимание! Будь осторожен с кодом, которому ты не доверяешь. Обязательно прочти [Как использовать TensorFlow безопасно?](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md)\n",
    "\n",
    "### Варианты\n",
    "\n",
    "Существуют разные способы сохранять модели TensorFlow - все зависит от API, которые ты использовал в своей модели. В этом уроке используется [tf.keras](https://www.tensorflow.org/guide/keras), высокоуровневый API для построения и обучения моделей в TensorFlow. Для всех остальных подходов читай руководство по TensorFlow  [Сохраняй и загружай модели](https://www.tensorflow.org/guide/saved_model) или [Сохранение в Eager](https://www.tensorflow.org/guide/eager#object_based_saving)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xCUREq7WXgvg"
   },
   "source": [
    "## Настройка\n",
    "\n",
    "### Настроим и импортируем зависимости"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "7l0MiTOrXtNv"
   },
   "source": [
    "Установим и импортируем TensorFlow и все зависимые библиотеки:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "RzIOVSdnMYyO"
   },
   "outputs": [],
   "source": [
    "!pip install h5py pyyaml "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "SbGsznErXWt6"
   },
   "source": [
    "### Загрузим датасет\n",
    "\n",
    "Мы воспользуемся [датасетом MNIST](http://yann.lecun.com/exdb/mnist/) для обучения нашей модели, чтобы показать как сохранять веса. Ускорим процесс, используя только первые 1000 образцов данных:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "7Nm7Tyb-gRt-"
   },
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function\n",
    "\n",
    "import os\n",
    "\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "\n",
    "tf.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "9rGfFwE9XVwz"
   },
   "outputs": [],
   "source": [
    "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n",
    "\n",
    "train_labels = train_labels[:1000]\n",
    "test_labels = test_labels[:1000]\n",
    "\n",
    "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n",
    "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "anG3iVoXyZGI"
   },
   "source": [
    "### Построим модель"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "wynsOBfby0Pa"
   },
   "source": [
    "Давай построим простую модель, на которой мы продемонстрируем как сохранять и загружать веса моделей:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "0HZbJIjxyX1S"
   },
   "outputs": [],
   "source": [
    "# Возвращает короткую последовательную модель\n",
    "def create_model():\n",
    "  model = tf.keras.models.Sequential([\n",
    "    keras.layers.Dense(512, activation=tf.nn.relu, input_shape=(784,)),\n",
    "    keras.layers.Dropout(0.2),\n",
    "    keras.layers.Dense(10, activation=tf.nn.softmax)\n",
    "  ])\n",
    "  \n",
    "  model.compile(optimizer=tf.train.AdamOptimizer(), \n",
    "                loss=tf.keras.losses.sparse_categorical_crossentropy,\n",
    "                metrics=['accuracy'])\n",
    "  \n",
    "  return model\n",
    "\n",
    "\n",
    "# Создадим модель\n",
    "model = create_model()\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "soDE0W_KH8rG"
   },
   "source": [
    "## Сохраняем контрольные точки"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "mRyd5qQQIXZm"
   },
   "source": [
    "Основная задача заключается в том, чтобы автоматически сохранять модель как *во время*, так и *по окончании* обучения. Таким образом ты сможешь снова использовать модель без необходимости обучать ее заново, или просто продолжить с места, на котором обучение было приостановлено.\n",
    "\n",
    "Эту задачу выполняет функция обратного вызова `tf.keras.callbacks.ModelCheckpoint`. Эта функция также может быть настроена при помощи нескольких аргументов.\n",
    "\n",
    "### Использование функции\n",
    "\n",
    "Обучим нашу модель и передадим ей функцию `ModelCheckpoint`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "IFPuhwntH8VH"
   },
   "outputs": [],
   "source": [
    "checkpoint_path = \"training_1/cp.ckpt\"\n",
    "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
    "\n",
    "# Создадим контрольную точку при помощи callback функции\n",
    "cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, \n",
    "                                                 save_weights_only=True,\n",
    "                                                 verbose=1)\n",
    "\n",
    "model = create_model()\n",
    "\n",
    "model.fit(train_images, train_labels,  epochs = 10, \n",
    "          validation_data = (test_images,test_labels),\n",
    "          callbacks = [cp_callback])  # передаем callback обучению"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "rlM-sgyJO084"
   },
   "source": [
    "Это создаст одну совокупность файлов контрольных точек TensorFlow, которые обновлялись в конце каждой эпохи:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "gXG5FVKFOVQ3"
   },
   "outputs": [],
   "source": [
    "!ls {checkpoint_dir}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "wlRN_f56Pqa9"
   },
   "source": [
    "Теперь создадим новую необученную модель. Когда мы восстанавливаем модель только из весов, новая модель должна быть точно такой же структуры, как и старая. Посколько архитектура модель точно такая же, то мы можем опубликовать веса из другой *инстанции* модели.\n",
    "\n",
    "Также мы оценим точность новой модели на проверочных данных. Необученная модель будет лишь изредка угадывать правильную категорию обзоров фильмов (точность будет около 10%):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Fp5gbuiaPqCT"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "\n",
    "loss, acc = model.evaluate(test_images, test_labels)\n",
    "print(\"Необученная модель, точность: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "1DTKpZssRSo3"
   },
   "source": [
    "А теперь загрузим веса из контрольной точки и проверим еще раз:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "2IZxbwiRRSD2"
   },
   "outputs": [],
   "source": [
    "model.load_weights(checkpoint_path)\n",
    "loss,acc = model.evaluate(test_images, test_labels)\n",
    "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "bpAbKkAyVPV8"
   },
   "source": [
    "### Параметры вызова контрольной точки\n",
    "\n",
    "У callback функции есть несколько параметров, которые дают контрольным точкам уникальные имена, а также корректируют частоту сохранения.\n",
    "\n",
    "Обучим новую модель и укажем параметр чтобы сохранять контрольные точки через каждые 5 эпох:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "mQF_dlgIVOvq"
   },
   "outputs": [],
   "source": [
    "# Укажем эпоху в имени файла (переведем ее в строки при помощи `str.format`)\n",
    "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n",
    "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
    "\n",
    "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n",
    "    checkpoint_path, verbose=1, save_weights_only=True,\n",
    "    # Сохраняем веса через каждые 5 эпох\n",
    "    period=5)\n",
    "\n",
    "model = create_model()\n",
    "model.fit(train_images, train_labels,\n",
    "          epochs = 50, callbacks = [cp_callback],\n",
    "          validation_data = (test_images,test_labels),\n",
    "          verbose=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "1zFrKTjjavWI"
   },
   "source": [
    "Теперь посмотрим на получившиеся контрольные точки и выберем последнюю:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "p64q3-V4sXt0"
   },
   "outputs": [],
   "source": [
    "! ls {checkpoint_dir}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "1AN_fnuyR41H"
   },
   "outputs": [],
   "source": [
    "latest = tf.train.latest_checkpoint(checkpoint_dir)\n",
    "latest"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Zk2ciGbKg561"
   },
   "source": [
    "Помни: по умолчанию TensorFlow сохраняет только 5 последних контрольных точек.\n",
    "\n",
    "Для проверки восстановим модель по умолчанию и загрузим последнюю контрольную точку:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "3M04jyK-H3QK"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "model.load_weights(latest)\n",
    "loss, acc = model.evaluate(test_images, test_labels)\n",
    "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "c2OxsJOTHxia"
   },
   "source": [
    "## Как выглядят эти файлы?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "JtdYhvWnH2ib"
   },
   "source": [
    "Код выше сохраняет веса модели как совокупность [контрольных точек](https://www.tensorflow.org/guide/saved_model#save_and_restore_variables) - форматированных файлов, которые содержат только обученные веса в двоичном формате. Они включают в себя:\n",
    "* Один или несколько шардов (shard, пер. \"Часть данных\"), в которых хранятся веса твоей модели\n",
    "* Индекс, который указывает какие веса хранятся в каждом шарде\n",
    "\n",
    "Если ты обучаешь модель на одном компьютере, то тогда у тебя будет всего один шард, оканчивающийся на `.data-00000-of-00001`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "S_FA-ZvxuXQV"
   },
   "source": [
    "## Сохраняем веса вручную\n",
    "\n",
    "Выше мы посмотрели как загружать веса в модель.\n",
    "\n",
    "Сохранять веса вручную так же просто, просто воспользуйся методом `Model.save_weights`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "R7W5plyZ-u9X"
   },
   "outputs": [],
   "source": [
    "# Сохраняем веса\n",
    "model.save_weights('./checkpoints/my_checkpoint')\n",
    "\n",
    "# Восстанавливаем веса\n",
    "model = create_model()\n",
    "model.load_weights('./checkpoints/my_checkpoint')\n",
    "\n",
    "loss,acc = model.evaluate(test_images, test_labels)\n",
    "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kOGlxPRBEvV1"
   },
   "source": [
    "## Сохраняем модель целиком\n",
    "\n",
    "Ты также можешь сохранить модель целиком в единый файл, который будет содержать все веса, конфигурацию модели и даже оптимизатор конфигурации (однако это зависит от выбранных параметров). Это позволит тебе восстановить модель и продолжить обучение позже, ровно с того момента, где ты остановился, и без правки изначального кода.\n",
    "\n",
    "Сохранять рабочую модель полностью весьма полезно. Например, ты можешь потом восстановить ее в TensorFlow.js ([HDF5](https://js.tensorflow.org/tutorials/import-keras.html), [Сохраненные модели](https://js.tensorflow.org/tutorials/import-saved-model.html)) и затем обучать и запускать ее в веб-браузерах, или конвертировать ее в формат для мобильных устройств, используя TensorFlow Lite ([HDF5](https://www.tensorflow.org/lite/convert/python_api#exporting_a_tfkeras_file_), [Сохраненные модели](https://www.tensorflow.org/lite/convert/python_api#exporting_a_savedmodel_))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "SkGwf-50zLNn"
   },
   "source": [
    "### Сохраняем в формате HDF5\n",
    "\n",
    "В Keras есть встроенный формат для сохранения модель при помощи стандарта [HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format). Для наших целей сохраненная модель будет использована как единый двоичный объект *blob*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "m2dkmJVCGUia"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "\n",
    "# Используй keras.optimizer чтобы восстановить оптимизатор из файла HDF5\n",
    "model.compile(optimizer=keras.optimizers.Adam(), \n",
    "              loss=tf.keras.losses.sparse_categorical_crossentropy,\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "model.fit(train_images, train_labels, epochs=5)\n",
    "\n",
    "# Сохраним модель полностью в единый HDF5 файл\n",
    "model.save('my_model.h5')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "GWmttMOqS68S"
   },
   "source": [
    "Теперь воссоздадим модель из этого файла:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "5NDMO_7kS6Do"
   },
   "outputs": [],
   "source": [
    "# Воссоздадим точно такую же модель, включая веса и оптимизатор:\n",
    "new_model = keras.models.load_model('my_model.h5')\n",
    "new_model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "JXQpbTicTBwt"
   },
   "source": [
    "Проверим ее точность:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "jwEaj9DnTCVA"
   },
   "outputs": [],
   "source": [
    "loss, acc = new_model.evaluate(test_images, test_labels)\n",
    "print(\"Восстановленная модель, точность: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "dGXqd4wWJl8O"
   },
   "source": [
    "Данная техника сохраняет все:\n",
    "\n",
    "* Веса модели\n",
    "* Конфигурацию (ее структуру)\n",
    "* Параметры оптимизатора\n",
    "\n",
    "Keras сохраняет модели путем исследования ее архтикетуры. В настоящее время он не может сохранять оптимизаторы TensorFlow из `tf.train`. Когда ты используешь их тебе будет нужно скомпилировать модель еще раз после загрузки. Таким образом ты получишь параметры оптимизатора.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "kPyhgcoVzqUB"
   },
   "source": [
    "### Сохраняем как `saved_model`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "LtcN4VIb7JkK"
   },
   "source": [
    "Обрати внимание: этот метод сохранения моделей `tf.keras` является экспериментальным и может измениться в будущих версиях."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DSWiSB0Q8c46"
   },
   "source": [
    "Построим новую модель:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "sI1YvCDFzpl3"
   },
   "outputs": [],
   "source": [
    "model = create_model()\n",
    "\n",
    "model.fit(train_images, train_labels, epochs=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "iUvT_3qE8hV5"
   },
   "source": [
    "Создадим `saved_model`: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "sq8fPglI1RWA"
   },
   "outputs": [],
   "source": [
    "saved_model_path = tf.contrib.saved_model.save_keras_model(model, \"./saved_models\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "MjpmyPfh8-1n"
   },
   "source": [
    "Сохраненные модели будут помещены в папку и отмечены текущей датой и временем в названии:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ZtOvxA7V0iTv"
   },
   "outputs": [],
   "source": [
    "!ls saved_models/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "B7qfpvpY9HCe"
   },
   "source": [
    "Загрузим новую модель Keras из уже сохраненной:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "0YofwHdN0pxa"
   },
   "outputs": [],
   "source": [
    "new_model = tf.contrib.saved_model.load_keras_model(saved_model_path)\n",
    "new_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "uWwgNaz19TH2"
   },
   "source": [
    "Запустим загруженную модель:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Pc9e6G6w1AWG"
   },
   "outputs": [],
   "source": [
    "# Оптимизатор не был восстановлен, поэтому мы укажим новый\n",
    "new_model.compile(optimizer=tf.train.AdamOptimizer(), \n",
    "              loss=tf.keras.losses.sparse_categorical_crossentropy,\n",
    "              metrics=['accuracy'])\n",
    "\n",
    "loss, acc = new_model.evaluate(test_images, test_labels)\n",
    "print(\"Загруженная модель, точность: {:5.2f}%\".format(100*acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "eUYTzSz5VxL2"
   },
   "source": [
    "## Что дальше?\n",
    "\n",
    "Это был короткий урок по сохранению и загрузке своих моделей при помощи `tf.kers`.\n",
    "\n",
    "* В [руководстве по tf.keras](https://www.tensorflow.org/guide/keras) рассказывается подробнее о том, как можно сохранять и загружать модели при помощи `tf.keras`\n",
    "\n",
    "* Статья [Сохраняй в Eager](https://www.tensorflow.org/guide/eager#object_based_saving) рассказывает как сохранять модель во время Eager Execution\n",
    "\n",
    "* В руководстве [Сохраняй и загружай модели](https://www.tensorflow.org/guide/saved_model) содержится подробный урок обо всех низкоуровневых деталях сохранения моделей TensorFlow"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "name": "save_and_restore_models.ipynb",
   "private_outputs": true,
   "provenance": [],
   "toc_visible": true,
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
